library(knitr)
library(fpp3)
library(readxl)
library(httr)
library(glue)
#install.packages("corrr")
library(corrr)
library(slider)
library(forcats)
#devtools::install_github('smin95/smplot2', force = TRUE)
library(smplot2)
#install.packages('sf')
library(sf)
#install.packages('Rcpp')
library(Rcpp)
#install.packages('mapview')
library(mapview)
Import Data & Create tsibble
df<-readr::read_csv('df_bike_violations_2023-06-29.csv')
Rows: 122620 Columns: 14── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): VIOLATION_CODE, VEH_CATEGORY, CITY_NM, Location, DESCRIPTION
dbl (8): EVNT_KEY, RPT_OWNING_CMD, X_COORD_CD, Y_COORD_CD, Latitude, Longitude, daily_total_cyclists, weekly_total_cyclists
dttm (1): VIOLATION_DATE
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
df<-df %>%
mutate(VIOLATION_CODE = as.factor(VIOLATION_CODE)) %>%
mutate(VEH_CATEGORY = as.factor(VEH_CATEGORY)) %>%
mutate(CITY_NM = as.factor(CITY_NM)) %>%
mutate(RPT_OWNING_CMD = as.factor(RPT_OWNING_CMD)) %>%
mutate(VIOLATION_DATE = as.POSIXct(VIOLATION_DATE))
# create a daily data tsibble
ts_daily_total <- df %>%
mutate(VIOLATION_DATE = as_date(VIOLATION_DATE)) %>%
group_by(VIOLATION_DATE, daily_total_cyclists) %>%
summarize(daily_total_violations = sum(n())) %>%
ungroup() %>%
as_tsibble(index = VIOLATION_DATE)
`summarise()` has grouped output by 'VIOLATION_DATE'. You can override using the `.groups` argument.
Clean daily tsibble
# check for missing dates # ans: 24
ts_daily_total %>%
#filter(is.na(daily_total_cyclists)) %>%
count_gaps()
# are the days missing from df_counts? # ans: no.
#df_counts %>%
# filter(is.na(daily_total))
# fill gaps dates and re-join with df_counts data, so that gapped dates now have total cyclsits. Replace violation na's with 0s.
ts_daily_total<-ts_daily_total %>%
fill_gaps() %>%
select(-daily_total_cyclists) %>%
mutate(date = VIOLATION_DATE) %>%
left_join(y=df_counts, by='date', multiple='any') %>%
rename(daily_total_cyclists = daily_total) %>%
select(-c('date', 'counts', 'weekly_total')) %>%
mutate_at('daily_total_violations', ~replace_na(., 0))
# double check for na's:
ts_daily_total %>%
filter(is.na(daily_total_cyclists))
# double check for date gaps:
ts_daily_total %>%
count_gaps()
NA
Create monthly data tsibble
# create a monthly data tsibble from cleaned daily tsibble
ts_monthly_total<-ts_daily_total %>%
index_by(yearmonth(VIOLATION_DATE)) %>%
summarize(across(c('daily_total_violations', 'daily_total_cyclists'), ~sum(.x, na.rm = TRUE))) %>%
rename(monthly_total_cyclists = daily_total_cyclists) %>%
rename(monthly_total_violations = daily_total_violations) %>%
rename(VIOLATION_DATE='yearmonth(VIOLATION_DATE)')
EDA
EDA: General
What’s the date range for the data?
date(range(df$VIOLATION_DATE))
[1] "2018-01-01" "2023-03-31"
Which dates had the highest number of riders, and how many?
df %>%
mutate(VIOLATION_DATE = date(VIOLATION_DATE)) %>%
arrange(desc(daily_total_cyclists)) %>%
distinct(VIOLATION_DATE, daily_total_cyclists) %>%
slice(1:10)
# takeaway: no particular trends, but mostly late summer / early autumn. 4 from September 2022.
Which dates had the highest number of violations, and how many
violations?
df %>%
mutate(VIOLATION_DATE = date(VIOLATION_DATE)) %>%
group_by(VIOLATION_DATE) %>%
summarize(daily_total_violations = sum(n())) %>%
arrange(desc(daily_total_violations)) %>%
slice(1:10)
# mostly warmer days in 2018 and 2019, pre-pandemic
EDA: Violation Codes
How many different violations were handed out to cyclist?
df %>%
distinct(VIOLATION_CODE)
# ans: 184
How many total violations were handed out to cyclists?
length(df$VIOLATION_CODE)
[1] 122620
# there were zero nas.
#sum(is.na(df$VIOLATION_CODE))
# ans: 122620
What were the most popular violations?
df %>%
group_by(VIOLATION_CODE, DESCRIPTION) %>%
summarize(sum = sum(n())) %>%
arrange(desc(sum)) %>%
ungroup() %>%
mutate(percent = 100*round(sum/sum(sum), 3)) %>%
slice(1:10)
`summarise()` has grouped output by 'VIOLATION_CODE'. You can override using the `.groups` argument.
# takewaways: 44% were for failing to stop at red light.
# note: 12332:ATTACHING SELF TO MOVING MOTOR VEHICLE a/k/a, the Marty McFly rule
EDA: daily cyclists
Plot: daily total cyclists over time
ts_daily_total %>%
autoplot(daily_total_cyclists)+
stat_smooth(method = "lm")

# takeaway: ridership did not change much pre or post pandemic. not even during lockdown, although possible hickup.
# However, ridership seems to be increaseing in the colder months while remaining level in the warmer months.
Plot: daily total cyclists histogram
ts_daily_total %>%
ggplot(aes(x=daily_total_cyclists))+
geom_histogram(bins=80)

Does ridership increase or decrease yearly?
# leave out 2023 since we only have up to March
ts_daily_total %>%
index_by(year(VIOLATION_DATE)) %>%
summarize(across(c('daily_total_violations', 'daily_total_cyclists'), ~sum(.x, na.rm = TRUE))) %>%
rename(yearly_total_cyclists = daily_total_cyclists) %>%
rename(yearly_total_violations = daily_total_violations) %>%
rename(VIOLATION_DATE='year(VIOLATION_DATE)') %>%
filter_index(.~'2022') %>%
autoplot(yearly_total_cyclists)

# Takeaway: Yes, it increases. slight dip in 2019 but clearly increasing
Does ridership change throughout the year (seasonality)?
# yearly seasonal plot
ts_daily_total %>%
gg_season(daily_total_cyclists)

# takeaway - it's roughly the same yearly cycle per year. increase in ridership from May-Oct, decrease Nov-Apr
# same as above but monthly totals, looks cleaner:
ts_monthly_total %>%
gg_season(monthly_total_cyclists)

# takeaway - dip in April 2020 during covid lockdown, but not much less than before.
# looking at it by month:
ts_monthly_total %>%
gg_subseries(monthly_total_cyclists)

# takeaways:
## seasonality is a little clearer. monthly difference is clearer.
## 2023 is turning out to be noticeably more riders than previous years.
# Ridership changes over time takeways:
## Ridership is increasing.
## Clear yearly seasonality, with May-Oct being the busiest, and Nov-Apr being less busy.
## Not much visual difference pre- and post- lockdown/pandemic.
Daily total cyclists decomposition
# classical decomp doesnt handle multi seasonality too well
ts_daily_total %>%
# filter(!is.na(daily_total_cyclists)) %>%
# fill_gaps() %>%
model(classical_decomposition(daily_total_cyclists, type = "additive")) %>%
components() %>%
autoplot()

# STL - best so far
ts_daily_total %>%
model(
STL(daily_total_cyclists ~ trend(window = 365) +
season(period='year')+season(period='1 month'),
)) %>%
components() %>%
autoplot()

# what about moving average for trend? it ok. I think stl is best.
ts_daily_total %>%
mutate(`5-MA` = slider::slide_dbl(daily_total_cyclists, mean, .before = 90, .after = 90, .complete = TRUE)) %>%
autoplot(daily_total_cyclists)+
geom_line(aes(y = `5-MA`), colour = "#D55E00")

# what about just a regular regression line
ts_daily_total %>%
autoplot(daily_total_cyclists)+
stat_smooth(method = "lm")

NA
NA
EDA: daily violations
Plot: daily total violations over time
ts_daily_total %>%
autoplot(daily_total_violations)

# takeaway: There were more violations before the pandemic. During lockdown, very few.
Plot: daily total violations historgram
ts_daily_total %>%
ggplot(aes(x=daily_total_violations))+
geom_histogram(bins=80)

Are the yearly number of violations trending up or down?
ts_daily_total %>%
#filter_index('2020'~.) %>%
index_by(year(VIOLATION_DATE)) %>%
summarize(across(c('daily_total_violations', 'daily_total_cyclists'), ~sum(.x, na.rm = TRUE))) %>%
rename(yearly_total_cyclists = daily_total_cyclists) %>%
rename(yearly_total_violations = daily_total_violations) %>%
rename(VIOLATION_DATE='year(VIOLATION_DATE)') %>%
filter_index(.~'2022') %>%
autoplot(yearly_total_violations)

# takeaway: not reall, post-covid. It looks steady. 2020, even with lockdown, had more violations than 2021 and 2022, but less than 2019.
# monthly: just cleaner graph of daily violations over time. Post-covid looks steady.
ts_monthly_total %>%
autoplot(monthly_total_violations)

Do number of violations change throughout the year (seasonal)?
# just post-covid
ts_daily_total %>%
filter_index('2020-03'~.) %>%
gg_season(daily_total_violations)

# is it easier to see monthly?
ts_monthly_total %>%
filter_index('2020-03'~.) %>%
gg_season(monthly_total_violations)

# subseries plot
ts_monthly_total %>%
filter_index('2020-03'~.) %>%
gg_subseries(monthly_total_violations)

# takeaway - Hard to see any trends, but fewer violations in Nov/Dec/Jan. Most in Sept Oct. 2021 was a steady year.
As the number of riders increases, does the number of violations
also increase?
# utilizes corrr library
df %>%
mutate(VIOLATION_DATE = date(VIOLATION_DATE)) %>%
group_by(VIOLATION_DATE) %>%
mutate(daily_total_violations = sum(n())) %>%
select(daily_total_cyclists, daily_total_violations) %>%
correlate()
Adding missing grouping variables: `VIOLATION_DATE`Non-numeric variables removed from input: `VIOLATION_DATE`Correlation computed with
• Method: 'pearson'
• Missing treated using: 'pairwise.complete.obs'
# takeaawy cor: 0.2159162 So no, as the number of riders increases, the number of violations does not.
Plot: daily violations vs. daily cylists
ts_daily_total %>%
ggplot(aes(x=daily_total_violations, y=daily_total_cyclists))+
geom_point()

Does this differ for pre-covid and post-lockdown?
Plot: Pre-covid

Plot: Post-covid
# correlation: 0.3846224
ts_daily_total %>%
filter_index("2020-04"~.) %>%
select(daily_total_cyclists, daily_total_violations) %>%
correlate()
Non-numeric variables removed from input: `VIOLATION_DATE`Correlation computed with
• Method: 'pearson'
• Missing treated using: 'pairwise.complete.obs'
Do largest violations (summed per day) change over time?
df %>%
mutate(fct_lump = fct_lump(f=df$VIOLATION_CODE, n=9)) %>%
filter(fct_lump=='1111D1C') %>%
mutate(date = date(VIOLATION_DATE)) %>%
filter(date>='2020-04-01') %>%
group_by(date, fct_lump) %>%
summarize(sum=sum(n())) %>%
ggplot(aes(x=date, y=sum))+
geom_line()
`summarise()` has grouped output by 'date'. You can override using the `.groups` argument.

To Do: Did the type of violations change after covid?
To do: Geocode violations. See where most of them occur.
To do: Can we predict ridership?
To do: Can we predict daily violations?
scratchpad
# cyclist counters per borough:
#library(forcats)
df_bicycle_counters_boroughs %>%
ggplot(aes(x=fct_infreq(Borough))) +
geom_bar()+
labs(x = "Borough", y="Cyclist Counters")
Error in `geom_bar()`:
! Problem while computing aesthetics.
ℹ Error occurred in the 1st layer.
Caused by error in `check_factor()`:
! object 'Borough' not found
Backtrace:
1. base (local) `<fn>`(x)
18. forcats::fct_infreq(Borough)
19. forcats:::check_factor(f)

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoZnBwMykKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoaHR0cikKbGlicmFyeShnbHVlKQojaW5zdGFsbC5wYWNrYWdlcygiY29ycnIiKQpsaWJyYXJ5KGNvcnJyKQpsaWJyYXJ5KHNsaWRlcikKbGlicmFyeShmb3JjYXRzKQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdzbWluOTUvc21wbG90MicsIGZvcmNlID0gVFJVRSkKbGlicmFyeShzbXBsb3QyKQojaW5zdGFsbC5wYWNrYWdlcygnc2YnKQpsaWJyYXJ5KHNmKQojaW5zdGFsbC5wYWNrYWdlcygnUmNwcCcpCmxpYnJhcnkoUmNwcCkKI2luc3RhbGwucGFja2FnZXMoJ21hcHZpZXcnKQpsaWJyYXJ5KG1hcHZpZXcpCmBgYAoKCiMjIEltcG9ydCBEYXRhICYgQ3JlYXRlIHRzaWJibGUKYGBge3J9CmRmPC1yZWFkcjo6cmVhZF9jc3YoJ2RmX2Jpa2VfdmlvbGF0aW9uc18yMDIzLTA2LTI5LmNzdicpCgpkZjwtZGYgJT4lIAogIG11dGF0ZShWSU9MQVRJT05fQ09ERSA9IGFzLmZhY3RvcihWSU9MQVRJT05fQ09ERSkpICU+JQogIG11dGF0ZShWRUhfQ0FURUdPUlkgPSBhcy5mYWN0b3IoVkVIX0NBVEVHT1JZKSkgJT4lIAogIG11dGF0ZShDSVRZX05NID0gYXMuZmFjdG9yKENJVFlfTk0pKSAlPiUgCiAgbXV0YXRlKFJQVF9PV05JTkdfQ01EID0gYXMuZmFjdG9yKFJQVF9PV05JTkdfQ01EKSkgJT4lIAogIG11dGF0ZShWSU9MQVRJT05fREFURSA9IGFzLlBPU0lYY3QoVklPTEFUSU9OX0RBVEUpKSAKCgojIGNyZWF0ZSBhIGRhaWx5IGRhdGEgdHNpYmJsZQp0c19kYWlseV90b3RhbCA8LSBkZiAlPiUgCiAgbXV0YXRlKFZJT0xBVElPTl9EQVRFID0gYXNfZGF0ZShWSU9MQVRJT05fREFURSkpICU+JSAKICBncm91cF9ieShWSU9MQVRJT05fREFURSwgZGFpbHlfdG90YWxfY3ljbGlzdHMpICU+JSAKICBzdW1tYXJpemUoZGFpbHlfdG90YWxfdmlvbGF0aW9ucyA9IHN1bShuKCkpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBhc190c2liYmxlKGluZGV4ID0gVklPTEFUSU9OX0RBVEUpCgpgYGAKIyMjIENsZWFuIGRhaWx5IHRzaWJibGUKYGBge3J9CiMgY2hlY2sgZm9yIG1pc3NpbmcgZGF0ZXMgICMgYW5zOiAyNAp0c19kYWlseV90b3RhbCAlPiUgCiAgI2ZpbHRlcihpcy5uYShkYWlseV90b3RhbF9jeWNsaXN0cykpICU+JSAKICBjb3VudF9nYXBzKCkKCiMgYXJlIHRoZSBkYXlzIG1pc3NpbmcgZnJvbSBkZl9jb3VudHM/ICAjIGFuczogbm8uCiNkZl9jb3VudHMgJT4lICAKIyAgZmlsdGVyKGlzLm5hKGRhaWx5X3RvdGFsKSkKCgoKIyBmaWxsIGdhcHMgZGF0ZXMgYW5kIHJlLWpvaW4gd2l0aCBkZl9jb3VudHMgZGF0YSwgc28gdGhhdCBnYXBwZWQgZGF0ZXMgbm93IGhhdmUgdG90YWwgY3ljbHNpdHMuIFJlcGxhY2UgdmlvbGF0aW9uIG5hJ3Mgd2l0aCAwcy4KdHNfZGFpbHlfdG90YWw8LXRzX2RhaWx5X3RvdGFsICU+JQogIGZpbGxfZ2FwcygpICU+JQogIHNlbGVjdCgtZGFpbHlfdG90YWxfY3ljbGlzdHMpICU+JSAKICBtdXRhdGUoZGF0ZSA9IFZJT0xBVElPTl9EQVRFKSAlPiUgCiAgbGVmdF9qb2luKHk9ZGZfY291bnRzLCBieT0nZGF0ZScsIG11bHRpcGxlPSdhbnknKSAlPiUgCiAgcmVuYW1lKGRhaWx5X3RvdGFsX2N5Y2xpc3RzID0gZGFpbHlfdG90YWwpICU+JSAKICBzZWxlY3QoLWMoJ2RhdGUnLCAnY291bnRzJywgJ3dlZWtseV90b3RhbCcpKSAlPiUgCiAgbXV0YXRlX2F0KCdkYWlseV90b3RhbF92aW9sYXRpb25zJywgfnJlcGxhY2VfbmEoLiwgMCkpCgoKIyBkb3VibGUgY2hlY2sgZm9yIG5hJ3M6ICAKdHNfZGFpbHlfdG90YWwgJT4lIAogIGZpbHRlcihpcy5uYShkYWlseV90b3RhbF9jeWNsaXN0cykpCgojIGRvdWJsZSBjaGVjayBmb3IgZGF0ZSBnYXBzOgp0c19kYWlseV90b3RhbCAlPiUgCiAgY291bnRfZ2FwcygpCgpgYGAKCiMjIyBDcmVhdGUgbW9udGhseSBkYXRhIHRzaWJibGUKCmBgYHtyfQojIGNyZWF0ZSBhIG1vbnRobHkgZGF0YSB0c2liYmxlIGZyb20gY2xlYW5lZCBkYWlseSB0c2liYmxlCnRzX21vbnRobHlfdG90YWw8LXRzX2RhaWx5X3RvdGFsICU+JSAKICBpbmRleF9ieSh5ZWFybW9udGgoVklPTEFUSU9OX0RBVEUpKSAlPiUgCiAgc3VtbWFyaXplKGFjcm9zcyhjKCdkYWlseV90b3RhbF92aW9sYXRpb25zJywgJ2RhaWx5X3RvdGFsX2N5Y2xpc3RzJyksICB+c3VtKC54LCBuYS5ybSA9IFRSVUUpKSkgJT4lIAogIHJlbmFtZShtb250aGx5X3RvdGFsX2N5Y2xpc3RzID0gZGFpbHlfdG90YWxfY3ljbGlzdHMpICU+JSAKICByZW5hbWUobW9udGhseV90b3RhbF92aW9sYXRpb25zID0gZGFpbHlfdG90YWxfdmlvbGF0aW9ucykgJT4lIAogIHJlbmFtZShWSU9MQVRJT05fREFURT0neWVhcm1vbnRoKFZJT0xBVElPTl9EQVRFKScpCgpgYGAKCgojIEVEQQoKIyMgRURBOiBHZW5lcmFsCgojIyMgV2hhdCdzIHRoZSBkYXRlIHJhbmdlIGZvciB0aGUgZGF0YT8gCmBgYHtyfQpkYXRlKHJhbmdlKGRmJFZJT0xBVElPTl9EQVRFKSkKYGBgCiMjIyBXaGljaCBkYXRlcyBoYWQgdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIHJpZGVycywgYW5kIGhvdyBtYW55PwpgYGB7cn0KZGYgJT4lIAogIG11dGF0ZShWSU9MQVRJT05fREFURSA9IGRhdGUoVklPTEFUSU9OX0RBVEUpKSAlPiUgCiAgYXJyYW5nZShkZXNjKGRhaWx5X3RvdGFsX2N5Y2xpc3RzKSkgJT4lCiAgZGlzdGluY3QoVklPTEFUSU9OX0RBVEUsIGRhaWx5X3RvdGFsX2N5Y2xpc3RzKSAlPiUgCiAgc2xpY2UoMToxMCkKCiMgdGFrZWF3YXk6IG5vIHBhcnRpY3VsYXIgdHJlbmRzLCBidXQgbW9zdGx5IGxhdGUgc3VtbWVyIC8gZWFybHkgYXV0dW1uLiAgNCBmcm9tIFNlcHRlbWJlciAyMDIyLgpgYGAKCiMjIyBXaGljaCBkYXRlcyBoYWQgdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIHZpb2xhdGlvbnMsIGFuZCBob3cgbWFueSB2aW9sYXRpb25zPwpgYGB7cn0KZGYgJT4lIAogIG11dGF0ZShWSU9MQVRJT05fREFURSA9IGRhdGUoVklPTEFUSU9OX0RBVEUpKSAlPiUgCiAgZ3JvdXBfYnkoVklPTEFUSU9OX0RBVEUpICU+JSAKICBzdW1tYXJpemUoZGFpbHlfdG90YWxfdmlvbGF0aW9ucyA9IHN1bShuKCkpKSAlPiUgCiAgYXJyYW5nZShkZXNjKGRhaWx5X3RvdGFsX3Zpb2xhdGlvbnMpKSAlPiUgCiAgc2xpY2UoMToxMCkKCgojIG1vc3RseSB3YXJtZXIgZGF5cyBpbiAyMDE4IGFuZCAyMDE5LCBwcmUtcGFuZGVtaWMKYGBgCiMjIEVEQTogVmlvbGF0aW9uIENvZGVzCgojIyMgSG93IG1hbnkgZGlmZmVyZW50IHZpb2xhdGlvbnMgd2VyZSBoYW5kZWQgb3V0IHRvIGN5Y2xpc3Q/CmBgYHtyfQpkZiAlPiUgCiAgZGlzdGluY3QoVklPTEFUSU9OX0NPREUpCiAgCiMgYW5zOiAxODQKYGBgCiMjIyBIb3cgbWFueSB0b3RhbCB2aW9sYXRpb25zIHdlcmUgaGFuZGVkIG91dCB0byBjeWNsaXN0cz8KYGBge3J9Cmxlbmd0aChkZiRWSU9MQVRJT05fQ09ERSkKCiMgdGhlcmUgd2VyZSB6ZXJvIG5hcy4gIAojc3VtKGlzLm5hKGRmJFZJT0xBVElPTl9DT0RFKSkKCgojIGFuczogMTIyNjIwCmBgYAoKCiMjIyBXaGF0IHdlcmUgdGhlIG1vc3QgcG9wdWxhciB2aW9sYXRpb25zPwoKYGBge3J9CmRmICU+JSAKICBncm91cF9ieShWSU9MQVRJT05fQ09ERSwgREVTQ1JJUFRJT04pICU+JSAKICBzdW1tYXJpemUoc3VtID0gc3VtKG4oKSkpICU+JSAKICBhcnJhbmdlKGRlc2Moc3VtKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKHBlcmNlbnQgPSAxMDAqcm91bmQoc3VtL3N1bShzdW0pLCAzKSkgJT4lIAogIHNsaWNlKDE6MTApCgogIAogICMgdGFrZXdhd2F5czogIDQ0JSB3ZXJlIGZvciBmYWlsaW5nIHRvIHN0b3AgYXQgcmVkIGxpZ2h0LgoKIyBub3RlOiAgMTIzMzI6QVRUQUNISU5HIFNFTEYgVE8gTU9WSU5HIE1PVE9SIFZFSElDTEUgYS9rL2EsIHRoZSBNYXJ0eSBNY0ZseSBydWxlCgpgYGAKCgoKCgoKIyMgRURBOiBkYWlseSBjeWNsaXN0cwoKIyMjIFBsb3Q6IGRhaWx5IHRvdGFsIGN5Y2xpc3RzIG92ZXIgdGltZQpgYGB7cn0KdHNfZGFpbHlfdG90YWwgJT4lIAogIGF1dG9wbG90KGRhaWx5X3RvdGFsX2N5Y2xpc3RzKSsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iKQoKIyB0YWtlYXdheTogcmlkZXJzaGlwIGRpZCBub3QgY2hhbmdlIG11Y2ggcHJlIG9yIHBvc3QgcGFuZGVtaWMuIG5vdCBldmVuIGR1cmluZyBsb2NrZG93biwgYWx0aG91Z2ggcG9zc2libGUgaGlja3VwLgojIEhvd2V2ZXIsIHJpZGVyc2hpcCBzZWVtcyB0byBiZSBpbmNyZWFzZWluZyBpbiB0aGUgY29sZGVyIG1vbnRocyB3aGlsZSByZW1haW5pbmcgbGV2ZWwgaW4gdGhlIHdhcm1lciBtb250aHMuCmBgYAojIyMgUGxvdDogZGFpbHkgdG90YWwgY3ljbGlzdHMgaGlzdG9ncmFtCmBgYHtyfQp0c19kYWlseV90b3RhbCAlPiUgCiAgZ2dwbG90KGFlcyh4PWRhaWx5X3RvdGFsX2N5Y2xpc3RzKSkrCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz04MCkKYGBgCgoKCgojIyMgRG9lcyByaWRlcnNoaXAgaW5jcmVhc2Ugb3IgZGVjcmVhc2UgeWVhcmx5PwpgYGB7cn0KIyBsZWF2ZSBvdXQgMjAyMyBzaW5jZSB3ZSBvbmx5IGhhdmUgdXAgdG8gTWFyY2gKCnRzX2RhaWx5X3RvdGFsICU+JSAKICBpbmRleF9ieSh5ZWFyKFZJT0xBVElPTl9EQVRFKSkgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoYygnZGFpbHlfdG90YWxfdmlvbGF0aW9ucycsICdkYWlseV90b3RhbF9jeWNsaXN0cycpLCAgfnN1bSgueCwgbmEucm0gPSBUUlVFKSkpICU+JSAKICByZW5hbWUoeWVhcmx5X3RvdGFsX2N5Y2xpc3RzID0gZGFpbHlfdG90YWxfY3ljbGlzdHMpICU+JSAKICByZW5hbWUoeWVhcmx5X3RvdGFsX3Zpb2xhdGlvbnMgPSBkYWlseV90b3RhbF92aW9sYXRpb25zKSAlPiUgCiAgcmVuYW1lKFZJT0xBVElPTl9EQVRFPSd5ZWFyKFZJT0xBVElPTl9EQVRFKScpICU+JSAKICBmaWx0ZXJfaW5kZXgoLn4nMjAyMicpICU+JSAKICBhdXRvcGxvdCh5ZWFybHlfdG90YWxfY3ljbGlzdHMpCgojIFRha2Vhd2F5OiBZZXMsIGl0IGluY3JlYXNlcy4gc2xpZ2h0IGRpcCBpbiAyMDE5IGJ1dCBjbGVhcmx5IGluY3JlYXNpbmcKCmBgYAoKCiMjIyBEb2VzIHJpZGVyc2hpcCBjaGFuZ2UgdGhyb3VnaG91dCB0aGUgeWVhciAoc2Vhc29uYWxpdHkpPwpgYGB7cn0KIyB5ZWFybHkgc2Vhc29uYWwgcGxvdAp0c19kYWlseV90b3RhbCAlPiUgCiAgZ2dfc2Vhc29uKGRhaWx5X3RvdGFsX2N5Y2xpc3RzKQojIHRha2Vhd2F5IC0gaXQncyByb3VnaGx5IHRoZSBzYW1lIHllYXJseSBjeWNsZSBwZXIgeWVhci4gIGluY3JlYXNlIGluIHJpZGVyc2hpcCBmcm9tIE1heS1PY3QsIGRlY3JlYXNlIE5vdi1BcHIKCiMgc2FtZSBhcyBhYm92ZSBidXQgbW9udGhseSB0b3RhbHMsIGxvb2tzIGNsZWFuZXI6CnRzX21vbnRobHlfdG90YWwgJT4lIAogIGdnX3NlYXNvbihtb250aGx5X3RvdGFsX2N5Y2xpc3RzKQojIHRha2Vhd2F5IC0gZGlwIGluIEFwcmlsIDIwMjAgZHVyaW5nIGNvdmlkIGxvY2tkb3duLCBidXQgbm90IG11Y2ggbGVzcyB0aGFuIGJlZm9yZS4KCgojIGxvb2tpbmcgYXQgaXQgYnkgbW9udGg6CnRzX21vbnRobHlfdG90YWwgJT4lIAogIGdnX3N1YnNlcmllcyhtb250aGx5X3RvdGFsX2N5Y2xpc3RzKQojIHRha2Vhd2F5czoKIyMgc2Vhc29uYWxpdHkgaXMgYSBsaXR0bGUgY2xlYXJlci4gIG1vbnRobHkgZGlmZmVyZW5jZSBpcyBjbGVhcmVyLgojIyAyMDIzIGlzIHR1cm5pbmcgb3V0IHRvIGJlIG5vdGljZWFibHkgbW9yZSByaWRlcnMgdGhhbiBwcmV2aW91cyB5ZWFycy4KCgojIFJpZGVyc2hpcCBjaGFuZ2VzIG92ZXIgdGltZSB0YWtld2F5czoKIyMgUmlkZXJzaGlwIGlzIGluY3JlYXNpbmcuCiMjIENsZWFyIHllYXJseSBzZWFzb25hbGl0eSwgd2l0aCBNYXktT2N0IGJlaW5nIHRoZSBidXNpZXN0LCBhbmQgTm92LUFwciBiZWluZyBsZXNzIGJ1c3kuCiMjIE5vdCBtdWNoIHZpc3VhbCBkaWZmZXJlbmNlIHByZS0gYW5kIHBvc3QtIGxvY2tkb3duL3BhbmRlbWljLiAgCgpgYGAKCiMjIyBEYWlseSB0b3RhbCBjeWNsaXN0cyBkZWNvbXBvc2l0aW9uCmBgYHtyfQoKIyBjbGFzc2ljYWwgZGVjb21wIGRvZXNudCBoYW5kbGUgbXVsdGkgc2Vhc29uYWxpdHkgdG9vIHdlbGwKdHNfZGFpbHlfdG90YWwgJT4lIAojICBmaWx0ZXIoIWlzLm5hKGRhaWx5X3RvdGFsX2N5Y2xpc3RzKSkgJT4lIAojICBmaWxsX2dhcHMoKSAlPiUgCiAgbW9kZWwoY2xhc3NpY2FsX2RlY29tcG9zaXRpb24oZGFpbHlfdG90YWxfY3ljbGlzdHMsIHR5cGUgPSAiYWRkaXRpdmUiKSkgJT4lIAoJY29tcG9uZW50cygpICU+JQogIGF1dG9wbG90KCkKCgojIFNUTCAtIGJlc3Qgc28gZmFyCnRzX2RhaWx5X3RvdGFsIAklPiUKCW1vZGVsKAogICAgU1RMKGRhaWx5X3RvdGFsX2N5Y2xpc3RzIH4gdHJlbmQod2luZG93ID0gMzY1KSArCiAgICAgICAgICAgICAgICAgICBzZWFzb24ocGVyaW9kPSd5ZWFyJykrc2Vhc29uKHBlcmlvZD0nMSBtb250aCcpLAogICAgKSkgJT4lCiAgY29tcG9uZW50cygpICU+JSAKICBhdXRvcGxvdCgpCgojIHdoYXQgYWJvdXQgbW92aW5nIGF2ZXJhZ2UgZm9yIHRyZW5kPyAgaXQgb2suICBJIHRoaW5rIHN0bCBpcyBiZXN0Lgp0c19kYWlseV90b3RhbCAlPiUgCiAgICBtdXRhdGUoYDUtTUFgID0gc2xpZGVyOjpzbGlkZV9kYmwoZGFpbHlfdG90YWxfY3ljbGlzdHMsIG1lYW4sIC5iZWZvcmUgPSA5MCwgLmFmdGVyID0gOTAsIC5jb21wbGV0ZSA9IFRSVUUpKSAlPiUKICAgIGF1dG9wbG90KGRhaWx5X3RvdGFsX2N5Y2xpc3RzKSsKICAgIGdlb21fbGluZShhZXMoeSA9IGA1LU1BYCksIGNvbG91ciA9ICIjRDU1RTAwIikKCiMgd2hhdCBhYm91dCBqdXN0IGEgcmVndWxhciByZWdyZXNzaW9uIGxpbmUKdHNfZGFpbHlfdG90YWwgJT4lIAogIGF1dG9wbG90KGRhaWx5X3RvdGFsX2N5Y2xpc3RzKSsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iKQoKCmBgYAoKIyMgRURBOiBkYWlseSB2aW9sYXRpb25zCgojIyMgUGxvdDogZGFpbHkgdG90YWwgdmlvbGF0aW9ucyBvdmVyIHRpbWUKYGBge3J9Cgp0c19kYWlseV90b3RhbCAlPiUgCiAgYXV0b3Bsb3QoZGFpbHlfdG90YWxfdmlvbGF0aW9ucykKCiMgdGFrZWF3YXk6ICBUaGVyZSB3ZXJlIG1vcmUgdmlvbGF0aW9ucyBiZWZvcmUgdGhlIHBhbmRlbWljLiAgRHVyaW5nIGxvY2tkb3duLCB2ZXJ5IGZldy4KCmBgYAoKIyMjIFBsb3Q6IGRhaWx5IHRvdGFsIHZpb2xhdGlvbnMgaGlzdG9yZ3JhbQpgYGB7cn0KdHNfZGFpbHlfdG90YWwgJT4lIAogIGdncGxvdChhZXMoeD1kYWlseV90b3RhbF92aW9sYXRpb25zKSkrCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz04MCkKYGBgCiMjIyBBcmUgdGhlIHllYXJseSBudW1iZXIgb2YgdmlvbGF0aW9ucyB0cmVuZGluZyB1cCBvciBkb3duPwpgYGB7cn0KdHNfZGFpbHlfdG90YWwgJT4lIAogICNmaWx0ZXJfaW5kZXgoJzIwMjAnfi4pICU+JSAKICBpbmRleF9ieSh5ZWFyKFZJT0xBVElPTl9EQVRFKSkgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoYygnZGFpbHlfdG90YWxfdmlvbGF0aW9ucycsICdkYWlseV90b3RhbF9jeWNsaXN0cycpLCAgfnN1bSgueCwgbmEucm0gPSBUUlVFKSkpICU+JSAKICByZW5hbWUoeWVhcmx5X3RvdGFsX2N5Y2xpc3RzID0gZGFpbHlfdG90YWxfY3ljbGlzdHMpICU+JSAKICByZW5hbWUoeWVhcmx5X3RvdGFsX3Zpb2xhdGlvbnMgPSBkYWlseV90b3RhbF92aW9sYXRpb25zKSAlPiUgCiAgcmVuYW1lKFZJT0xBVElPTl9EQVRFPSd5ZWFyKFZJT0xBVElPTl9EQVRFKScpICU+JSAKICBmaWx0ZXJfaW5kZXgoLn4nMjAyMicpICU+JSAKICBhdXRvcGxvdCh5ZWFybHlfdG90YWxfdmlvbGF0aW9ucykKICAKCiMgdGFrZWF3YXk6IG5vdCByZWFsbCwgcG9zdC1jb3ZpZC4gSXQgbG9va3Mgc3RlYWR5LiAgMjAyMCwgZXZlbiB3aXRoIGxvY2tkb3duLCBoYWQgbW9yZSB2aW9sYXRpb25zIHRoYW4gMjAyMSBhbmQgMjAyMiwgYnV0IGxlc3MgdGhhbiAyMDE5LgoKIyBtb250aGx5OiAganVzdCBjbGVhbmVyIGdyYXBoIG9mIGRhaWx5IHZpb2xhdGlvbnMgb3ZlciB0aW1lLiAgUG9zdC1jb3ZpZCBsb29rcyBzdGVhZHkuCnRzX21vbnRobHlfdG90YWwgJT4lIAogIGF1dG9wbG90KG1vbnRobHlfdG90YWxfdmlvbGF0aW9ucykKCmBgYAoKCgojIyMgRG8gbnVtYmVyIG9mIHZpb2xhdGlvbnMgY2hhbmdlIHRocm91Z2hvdXQgdGhlIHllYXIgKHNlYXNvbmFsKT8KYGBge3J9CiMganVzdCBwb3N0LWNvdmlkCnRzX2RhaWx5X3RvdGFsICU+JSAKICBmaWx0ZXJfaW5kZXgoJzIwMjAtMDMnfi4pICU+JSAKICBnZ19zZWFzb24oZGFpbHlfdG90YWxfdmlvbGF0aW9ucykKCgojIGlzIGl0IGVhc2llciB0byBzZWUgbW9udGhseT8KdHNfbW9udGhseV90b3RhbCAlPiUgCiAgZmlsdGVyX2luZGV4KCcyMDIwLTAzJ34uKSAlPiUgCiAgZ2dfc2Vhc29uKG1vbnRobHlfdG90YWxfdmlvbGF0aW9ucykKCiMgc3Vic2VyaWVzIHBsb3QKdHNfbW9udGhseV90b3RhbCAlPiUgCiAgZmlsdGVyX2luZGV4KCcyMDIwLTAzJ34uKSAlPiUgCiAgZ2dfc3Vic2VyaWVzKG1vbnRobHlfdG90YWxfdmlvbGF0aW9ucykKCiMgdGFrZWF3YXkgLSBIYXJkIHRvIHNlZSBhbnkgdHJlbmRzLCBidXQgZmV3ZXIgdmlvbGF0aW9ucyBpbiBOb3YvRGVjL0phbi4gIE1vc3QgaW4gU2VwdCBPY3QuICAyMDIxIHdhcyBhIHN0ZWFkeSB5ZWFyLgpgYGAKCgoKCiMjIyBBcyB0aGUgbnVtYmVyIG9mIHJpZGVycyBpbmNyZWFzZXMsIGRvZXMgdGhlIG51bWJlciBvZiB2aW9sYXRpb25zIGFsc28gaW5jcmVhc2U/CmBgYHtyfQoKIyB1dGlsaXplcyBjb3JyciBsaWJyYXJ5CgpkZiAlPiUgCiAgbXV0YXRlKFZJT0xBVElPTl9EQVRFID0gZGF0ZShWSU9MQVRJT05fREFURSkpICU+JSAKICBncm91cF9ieShWSU9MQVRJT05fREFURSkgJT4lIAogIG11dGF0ZShkYWlseV90b3RhbF92aW9sYXRpb25zID0gc3VtKG4oKSkpICU+JSAKICBzZWxlY3QoZGFpbHlfdG90YWxfY3ljbGlzdHMsIGRhaWx5X3RvdGFsX3Zpb2xhdGlvbnMpICU+JSAKICBjb3JyZWxhdGUoKQoKIyB0YWtlYWF3eSBjb3I6IDAuMjE1OTE2MiBTbyBubywgYXMgdGhlIG51bWJlciBvZiByaWRlcnMgaW5jcmVhc2VzLCB0aGUgbnVtYmVyIG9mIHZpb2xhdGlvbnMgZG9lcyBub3QuIAoKYGBgCiMjIyBQbG90OiBkYWlseSB2aW9sYXRpb25zIHZzLiBkYWlseSBjeWxpc3RzCmBgYHtyfQp0c19kYWlseV90b3RhbCAlPiUgCiAgZ2dwbG90KGFlcyh4PWRhaWx5X3RvdGFsX3Zpb2xhdGlvbnMsIHk9ZGFpbHlfdG90YWxfY3ljbGlzdHMpKSsKICBnZW9tX3BvaW50KCkKCmBgYAoKIyMjIERvZXMgdGhpcyBkaWZmZXIgZm9yIHByZS1jb3ZpZCBhbmQgcG9zdC1sb2NrZG93bj8KIyMjIyBQbG90OiBQcmUtY292aWQKYGBge3J9CgojIGNvcnJlbGF0aW9uOiAgMC41MDE3NDQKY29yPC10c19kYWlseV90b3RhbCAlPiUgCiAgZmlsdGVyX2luZGV4KH4iMjAyMC0wNCIpICU+JSAKICBzZWxlY3QoZGFpbHlfdG90YWxfY3ljbGlzdHMsIGRhaWx5X3RvdGFsX3Zpb2xhdGlvbnMpICU+JSAKICBjb3JyZWxhdGUoKSAlPiUgCiAgc2xpY2UoMSkgJT4lIAogIHNlbGVjdChkYWlseV90b3RhbF92aW9sYXRpb25zKSAlPiUgCiAgYXMubnVtZXJpYygpCgoKIyBwcmUtY292aWQKdHNfZGFpbHlfdG90YWwgJT4lIAogIGZpbHRlcl9pbmRleCh+IjIwMjAtMDQiKSAlPiUgCiAgZ2dwbG90KGFlcyh4PWRhaWx5X3RvdGFsX3Zpb2xhdGlvbnMsIHk9ZGFpbHlfdG90YWxfY3ljbGlzdHMpKSsKICBnZW9tX3BvaW50KCkrCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtKSsKICBzbV9zdGF0Q29ycihsYWJlbF94ID0gMjUwLCBsYWJlbF95ID0gMTAwMCkKICAKYGBgCgojIyMjIFBsb3Q6IFBvc3QtY292aWQKYGBge3J9CiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ3NtaW45NS9zbXBsb3QyJywgZm9yY2UgPSBUUlVFKQojbGlicmFyeShzbXBsb3QyKQoKIyBwb3N0LWNvdmlkCnRzX2RhaWx5X3RvdGFsICU+JSAKICBmaWx0ZXJfaW5kZXgoIjIwMjAtMDQifi4pICU+JSAKICBnZ3Bsb3QoYWVzKHg9ZGFpbHlfdG90YWxfdmlvbGF0aW9ucywgeT1kYWlseV90b3RhbF9jeWNsaXN0cykpKwogIGdlb21fcG9pbnQoKSsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0pKwogIHNtX3N0YXRDb3JyKGxhYmVsX3ggPSAxMjAsIGxhYmVsX3kgPSAxMDAwKQoKIyBjb3JyZWxhdGlvbjogIDAuMzg0NjIyNAp0c19kYWlseV90b3RhbCAlPiUgCiAgZmlsdGVyX2luZGV4KCIyMDIwLTA0In4uKSAlPiUgCiAgc2VsZWN0KGRhaWx5X3RvdGFsX2N5Y2xpc3RzLCBkYWlseV90b3RhbF92aW9sYXRpb25zKSAlPiUgCiAgY29ycmVsYXRlKCkKCiMgYW5zOiB0aGV5IGRpZmZlciBhIGxpdHRsZSBtb3JlIHRoYW4gYWxsIHRvZ2V0aGVyLiAgcHJlLWNvdmlkIHdhcyBhIGJldHRlciBpbmRpY2F0b3IuCmBgYAoKIyMjIERvIGxhcmdlc3QgdmlvbGF0aW9ucyAoc3VtbWVkIHBlciBkYXkpIGNoYW5nZSBvdmVyIHRpbWU/CmBgYHtyfQojIGNvZGUgdGVzdCB0byBsdW1wIGFsbCBleGNlcHQgMTAgaGlnaGVzdCBmYWN0b3JzIGludG8gJ290aGVyJyBjYXRlZ29yeQpkZiAlPiUgCiAgbXV0YXRlKGZjdF9sdW1wID0gZmN0X2x1bXAoZj1kZiRWSU9MQVRJT05fQ09ERSwgbj05KSkgJT4lIAogIGNvdW50KGZjdF9sdW1wKSAlPiUgCiAgYXJyYW5nZShkZXNjKG4pKQoKIyBwbG90OiBkYWlseSBiaWdnZXN0IHZpb2xhdGlvbiBvdmVyIHRpbWUsIHBvc3QgY292aWQKZGYgJT4lIAogIG11dGF0ZShmY3RfbHVtcCA9IGZjdF9sdW1wKGY9ZGYkVklPTEFUSU9OX0NPREUsIG49OSkpICU+JSAKICBmaWx0ZXIoZmN0X2x1bXA9PScxMTExRDFDJykgJT4lIAogIG11dGF0ZShkYXRlID0gZGF0ZShWSU9MQVRJT05fREFURSkpICU+JSAKICBmaWx0ZXIoZGF0ZT49JzIwMjAtMDQtMDEnKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgZmN0X2x1bXApICU+JSAKICBzdW1tYXJpemUoc3VtPXN1bShuKCkpKSAlPiUgCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9c3VtKSkrCiAgZ2VvbV9saW5lKCkKCgojIHRha2Vhd2F5OiAgTm8uICBub3QgbXVjaCBkaWZmZXJlbnQgdGhhbiBnZW5lcmFsIGRhaWx5IHZpb2xhdGlvbnMgcGxvdC4gIGxvd2VyIGFtb3VudCBhcm91bmQgRGVjL0phbiwgb3RoZXJ3aXNlIGZhaXJseSBzdGVhZHkuIAoKYGBgCgoKIyMjIFRvIERvOiBEaWQgdGhlIHR5cGUgb2YgdmlvbGF0aW9ucyBjaGFuZ2UgYWZ0ZXIgY292aWQ/CgpgYGB7cn0KCmBgYAoKCiMjIyBUbyBkbzogR2VvY29kZSB2aW9sYXRpb25zLiAgU2VlIHdoZXJlIG1vc3Qgb2YgdGhlbSBvY2N1ci4gCmBgYHtyfQoKCmRmX2ZjdF9sdW1wX3Bvc3RfY292aWQgPC0gZGYgJT4lIAogIG11dGF0ZShmY3RfbHVtcCA9IGZjdF9sdW1wKGY9ZGYkVklPTEFUSU9OX0NPREUsIG49OSkpICU+JSAKICBtdXRhdGUoZGF0ZT1kYXRlKFZJT0xBVElPTl9EQVRFKSkgJT4lIAogIGZpbHRlcihkYXRlPj0nMjAyMC0wNC0wMScpCgoKZGYgJT4lIAogICNtdXRhdGUoZmN0X2x1bXAgPSBmY3RfbHVtcChmPWRmJFZJT0xBVElPTl9DT0RFLCBuPTkpKSAlPiUgCiAgbXV0YXRlKGRhdGU9ZGF0ZShWSU9MQVRJT05fREFURSkpICU+JSAKICBzZWxlY3QoLVZJT0xBVElPTl9EQVRFKSAlPiUgCiAgZmlsdGVyKGRhdGU+PScyMDIwLTA0LTAxJykgJT4lIAogIG1hcHZpZXcobmEub21pdChkZiksIHhjb2wgPSAiTG9uZ2l0dWRlIiwgeWNvbCA9ICJMYXRpdHVkZSIsY3JzID0gNDI2OSwgZ3JpZD1GQUxTRSwgYWxwaGEucmVnaW9ucyA9IDAuMikKCgptYXB2aWV3KG5hLm9taXQoZGZfZmN0X2x1bXBfcG9zdF9jb3ZpZCksIHhjb2wgPSAiTG9uZ2l0dWRlIiwgeWNvbCA9ICJMYXRpdHVkZSIsemNvbD0nZmN0X2x1bXAnLCBjcnMgPSA0MjY5LCBncmlkPUZBTFNFLCBhbHBoYS5yZWdpb25zID0gMC4xKQoKIyBtYXAgaXMgdG9vIGZ1bGwsICBsZXQncyBqdXN0IHZpZXcgMjAyMi0wNCB0aHJvdWdoIDIwMjMtMDMKCmRmX2ZjdF9sdW1wX3ByZXZfeWVhcjwtZGYgJT4lIAogIG11dGF0ZShmY3RfbHVtcCA9IGZjdF9sdW1wKGY9ZGYkVklPTEFUSU9OX0NPREUsIG49OSkpICU+JSAKICBtdXRhdGUoZGF0ZT1kYXRlKFZJT0xBVElPTl9EQVRFKSkgJT4lIAogIGZpbHRlcihkYXRlPj0nMjAyMi0wNC0wMScpCgpkZl9mY3RfbHVtcF9wcmV2X3llYXIgJT4lIAogIGNvdW50KGZjdF9sdW1wKSAlPiUgCiAgYXJyYW5nZShkZXNjKG4pKQoKbWFwdmlldyhuYS5vbWl0KGRmX2ZjdF9sdW1wX3ByZXZfeWVhciksIHhjb2wgPSAiTG9uZ2l0dWRlIiwgeWNvbCA9ICJMYXRpdHVkZSIsemNvbD0nZmN0X2x1bXAnLCBjcnMgPSA0MjY5LCBncmlkPUZBTFNFLCBhbHBoYS5yZWdpb25zID0gMC44KQojIHRha2Vhd2F5OiBiZXR0ZXIsIGJ1dCBzdGlsbCBub3QgYXMgY2xlYXIgYXMgSSdkIGxpa2UuICAKCgojIExldCdzIGp1c3QgdmlldyBmY3RfbHVtcD09MTExMUQxQyBmb3IgdGhlIHByZXZpb3VzIHllYXI6CmRmX2ZjdF9sdW1wX3ByZXZfeWVhciAlPiUgCiAgZmlsdGVyKGZjdF9sdW1wPT0nMTExMUQxQycpICU+JSAKICBtYXB2aWV3KHhjb2wgPSAiTG9uZ2l0dWRlIiwgeWNvbCA9ICJMYXRpdHVkZSIsY3JzID0gNDI2OSwgZ3JpZD1GQUxTRSwgYWxwaGEucmVnaW9ucyA9IDAuMikKCgpkZiAlPiUgCiAgbXV0YXRlKGZjdF9sdW1wID0gZmN0X2x1bXAoZj1kZiRWSU9MQVRJT05fQ09ERSwgbj05KSkgJT4lIAogIG11dGF0ZShkYXRlPWRhdGUoVklPTEFUSU9OX0RBVEUpKSAlPiUgCiAgZmlsdGVyKGRhdGU+PScyMDIyLTA0LTAxJykgJT4lIAogIGZpbHRlcihmY3RfbHVtcD09JzExMTFEMUMnKSAlPiUgCiAgbWFwdmlldyh4Y29sID0gIkxvbmdpdHVkZSIsIHljb2wgPSAiTGF0aXR1ZGUiLGNycyA9IDQyNjksIGdyaWQ9RkFMU0UsIGFscGhhLnJlZ2lvbnMgPSAwLjIpCgojIHRha2Vhd2F5OiB3ZSBoYXZlIGEgYmV0dGVyIHZpZXcgb2YgdGhlIG1ham9yIHRpY2tldGluZyBhcmVhcyBmb3IgMTExMUQxQzogdXdzLCB1ZXMgKGFuZCB0aGVpciBjb3JyZXNwb25kaW5nIGF2ZW51ZXMgdXAgdGhyb3VnaCBIYXJsZW0pLCB1cCBhbmQgZG93biAxc3QgYW5kIDJuZCBhdmUsIGVzcGVjaWFseSBpbiB0aGUgZWFzdCB2aWxsYWdlLCBvdmVyIHRoZSBXYnVyZyBCcmlkZ2UsIHVwIGFuZCBkb3duIDR0aCBhbmQgNXRoIGF2ZXMgaW4gQnJvb2tseW4sIExpYmVydHkgQXZlIGluIE96b25lIHBhcmsvIFJpY2htb25kIEhpbGwsIEVhc3Qgc2lkZSBvZiBwcm9zcGVjdCBwYXJrIG9uIEJlZGZvcmQgQXZlLCBhbmQgYmVuc29uaHVyc3QsIGJrLgpgYGAKCgoKIyMjIFRvIGRvOiBDYW4gd2UgcHJlZGljdCByaWRlcnNoaXA/CmBgYHtyfQoKYGBgCgoKIyMjIFRvIGRvOiBDYW4gd2UgcHJlZGljdCBkYWlseSB2aW9sYXRpb25zPwpgYGB7cn0KCmBgYAoKCiMjIyMgc2NyYXRjaHBhZApgYGB7cn0Kc3RyKGRmKQoKIyBwbG90IHNob3dzIHRoYXQgZWJpa2VzIGFuZCBlc2Nvb3RlcnMgZGlkbnQgaGF2ZSB0aGVpciBvd24gY2F0ZWdvcmllcyB1bnRpbCBzb21ldGltZSBpbiBtaWQgMjAyMi4gYmVmb3JlIHRoYXQsIGV2ZXJ5dGhpbmcgd2FzIHVuZGVyICdiaWtlcycKZGYgJT4lIAogIGdncGxvdChhZXMoeD1WSU9MQVRJT05fREFURSwgeT1DSVRZX05NLCBjb2w9VkVIX0NBVEVHT1JZKSkrCiAgZ2VvbV9qaXR0ZXIoKQoKI251dGhpbgpkZiAlPiUgCiAgZ2dwbG90KGFlcyh4PVZJT0xBVElPTl9EQVRFLCB5PUNJVFlfTk0sIGNvbD1WSU9MQVRJT05fQ09ERSkpKwogIGdlb21faml0dGVyKCkKCiMgdmlvbGF0aW9ucyBwZXIgbW9udGgKZGYgJT4lIAogIGdyb3VwX2J5KHllYXJtb250aD15ZWFybW9udGgoVklPTEFUSU9OX0RBVEUpKSAlPiUgCiAgc3VtbWFyaXplKHRvdGFsX3Zpb2xhdGlvbnMgPSAgc3VtKG4oKSkpICU+JSAKICBnZ3Bsb3QoYWVzKHg9eWVhcm1vbnRoLCB5PXRvdGFsX3Zpb2xhdGlvbnMpKSsKICBnZW9tX2xpbmUoKQoKCiMgdmlvbGF0aW9ucyBwZXIgYm9yb3VnaCBwZXIgeWVhcgpkZiAlPiUgCiAgZ3JvdXBfYnkoeWVhcj15ZWFybW9udGgoVklPTEFUSU9OX0RBVEUpLCBDSVRZX05NKSAlPiUgCiAgc3VtbWFyaXplKHRvdGFsX3Zpb2xhdGlvbnMgPSAgc3VtKG4oKSkpICU+JSAKICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT10b3RhbF92aW9sYXRpb25zLCBjb2w9Q0lUWV9OTSkpKwogIGdlb21fbGluZSgpCgoKIyBjeWNsaXN0IGNvdW50ZXJzIHBlciBib3JvdWdoOgojbGlicmFyeShmb3JjYXRzKQpkZl9iaWN5Y2xlX2NvdW50ZXJzX2Jvcm91Z2hzICU+JSAKICBnZ3Bsb3QoYWVzKHg9ZmN0X2luZnJlcShCb3JvdWdoKSkpICsKICBnZW9tX2JhcigpKwogIGxhYnMoeCA9ICJCb3JvdWdoIiwgeT0iQ3ljbGlzdCBDb3VudGVycyIpCmBgYAoK